//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//
// (C) Copyright 2006 Marvell International Ltd.
// All Rights Reserved
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Module Name:  

serial_16650.cpp

Abstract:  

Type definitions and data for the serial port driver


Notes: 


--*/
#include <windows.h>
#include <types.h>
#include <ceddk.h>

#include <ddkreg.h>
#include <serhw.h>
#include <hw16550.h>
#include <Serdbg.h>

#include <monahans.h>
#include <xllp_gpio.h>
#include "16550.h"
#include <nkintr.h>
#include <Winbase.h>

static PAIRS s_HighWaterPairs[] = {
    {SERIAL_1_BYTE_HIGH_WATER, 1},
    {SERIAL_4_BYTE_HIGH_WATER, 8},
    {SERIAL_8_BYTE_HIGH_WATER, 16},
    {SERIAL_32_BYTE_HIGH_WATER, 32}
};


CPXAReg16550::CPXAReg16550(PPXA_UART_REG pRegAddr):CReg16550((PBYTE)pRegAddr, sizeof(DWORD))
{
    m_pRegAddr = (PPXA_UART_REG)pRegAddr;
}

void CPXAReg16550::Backup()
{
    CReg16550::Backup();
    m_SIRBackup = Read_SIR();
    // Backup happens during power off. So We need turn off the IRDA
    Write_SIR(0);
}

void CPXAReg16550::Restore()
{
    if (m_fIsBackedUp) {
        CReg16550::Restore();
        Write_SIR(m_SIRBackup);
    }
}

CPXAPdd16550::CPXAPdd16550(LPTSTR lpActivePath, PVOID pMdd, PHWOBJ pHwObj):CPdd16550(lpActivePath, pMdd, pHwObj)
{
    m_pBaseAddress = NULL;
    m_pGPIOReg = NULL;
    m_pDCCLKReg = NULL;
    m_pMFPReg = NULL;
    m_pMfpRMDb = NULL;	
	m_pOSTRegs = NULL; 
	
    m_MfpRMDbHandle = NULL;
    m_DMAC_TX_Handle = NULL;
    m_DMAC_RX_Handle = NULL;
    m_DMAEnabled = 0;
    m_32BitBusEnabled=0;
    m_AutoFlowControlEnabled=0;
    m_fIRConnected = FALSE;
    m_pMDDSerialHead = (PHW_INDEP_INFO) pMdd;
    
    ClientID = INVALID_CLIENT_ID;
}

CPXAPdd16550::~CPXAPdd16550()
{
    if (INVALID_CLIENT_ID != ClientID)
    {
        if (IPM_DeRegister(ClientID, 3000) == IPM_STATUS_SUCCESS)
        {
            RETAILMSG(1, (_T("[IPM %s] De-Registration Completed OK\n"), szName));
        }
        else
        {
            RETAILMSG(1, (_T("[IPM %s] De-Registration FAILED\n"), szName));
        }

        ClientID = INVALID_CLIENT_ID; 
    }

    if (m_DMAEnabled) {
        DMADeInit();
    }

    if (m_pBaseAddress) 
        MmUnmapIoSpace(m_pBaseAddress, sizeof(m_pBaseAddress));
    
    if (m_pGPIOReg)
        MmUnmapIoSpace(m_pGPIOReg, sizeof(m_pGPIOReg));
    
    if (m_pDCCLKReg)
        MmUnmapIoSpace(m_pDCCLKReg, sizeof(m_pDCCLKReg));

    if (m_pMFPReg)
        MmUnmapIoSpace((void *)m_pMFPReg,  0x700);

    if (m_pMfpRMDb)
        UnmapViewOfFile((void *)m_pMfpRMDb);

    if (m_pOSTRegs) 
        MmUnmapIoSpace(m_pOSTRegs, sizeof(m_pOSTRegs));

    if (m_MfpRMDbHandle)
        CloseHandle(m_MfpRMDbHandle);

}

BOOL CPXAPdd16550::Init()
{
    IPM_STATUS_T status;

    RETAILMSG(1, (_T("[UART]CPXAPdd16550::Init()\r\n")));
    RETAILMSG(UART_DEBUG,(_T("[%s] Sending IPM Registration Request \n"),szName));
    RETAILMSG(UART_DEBUG,(_T(" m_AutoFlowControlEnabled = %d, m_DMAEnabled = %d, m_UART_Handle.UARTSelection = %d\n"),
        m_AutoFlowControlEnabled, m_DMAEnabled, m_UART_Handle.UARTSelection));
    
    status = IPM_Register(szName, &(ClientID), 3000);

    if (status == IPM_STATUS_SUCCESS)
    {
        DWORD dwProcState;
        RETAILMSG(1,(_T("[%s] Registration Completed, client id is %d\r\n"),szName, ClientID));

        dwProcState = IPM_PROCESSOR_D0_RDY | IPM_PROCESSOR_RING_OSC_RDY | IPM_PROCESSOR_D2_RDY| IPM_PROCESSOR_D1_RDY; // Initial

        // Notify IPM 
        RETAILMSG(UART_DEBUG,(_T("[%s] Initial Notify IPM Ready For Processor state. dwProcState=%x \r\n"), szName,dwProcState));
        if (IPM_NotifyReadyForProcState(ClientID, dwProcState ,3000)==IPM_STATUS_SUCCESS)
        {
            RETAILMSG(1,(_T("[%s] Notify IPM Ready For Processor state for driver Success!\r\n"), szName));
        }
        else
        {
            RETAILMSG(UART_DEBUG,(_T("[%s] Notify IPM Ready For Processor state for driver FAILED\r\n"),szName));
        }
    }
    else
    {
        RETAILMSG(1,(_T("[%s] Registration Failed, IPM Error %d\n"), szName, status));
    }
        
    if (!GetRegValue(PC_REG_DEVINDEX_VAL_NAME, (PBYTE)&m_dwDevIndex, sizeof(m_dwDevIndex))) {
        m_dwDevIndex = 0;
    }

    if (!GetRegValue(PC_REG_SERIALIRCONNECTED_VAL_NAME,(PBYTE)&m_fIRConnected, sizeof(m_fIRConnected))) {
        m_fIRConnected=0;
    }

    if (!GetRegValue(PC_REG_SERIAL32BITBUS_VAL_NAME,(PBYTE)&m_32BitBusEnabled, sizeof(m_32BitBusEnabled))) {
        m_32BitBusEnabled=0;
    }

    if (!GetRegValue(PC_REG_SERIALWATERMARK_VAL_NAME,(PBYTE)&m_dwWaterMark, sizeof(m_dwWaterMark))) {
        m_dwWaterMark = 32; // Default to Half of FIFO.
    }
    
    if (CSerialPDD::Init() && IsKeyOpened() && m_XmitFlushDone!=NULL) 
    { 
        // IST Setup . This is only need when Root Bus driver does not allocate any resource for this driver.
        DDKISRINFO ddi;
        ddi.dwIrq = MAXDWORD; ddi.dwSysintr = MAXDWORD;
        DWORD dwRet;

        dwRet = GetIsrInfo(&ddi);

        if ((dwRet != ERROR_SUCCESS) 
            || (ddi.dwSysintr == SYSINTR_NOP) || (ddi.dwSysintr >= 0xff) || (ddi.dwIrq <= 0) || (ddi.dwIrq >=0xff))
        {

            RETAILMSG(UART_DEBUG, (TEXT("[UART] There is no static IRQ and SYSINTR translation for UART.  \r\n")));

            switch (m_UART_Handle.UARTSelection)
            {
                case XLLP_FFUART:
                    ddi.dwIrq=IRQ_FFUART;
                    break;
                case XLLP_BTUART:
                    ddi.dwIrq=IRQ_BTUART;
                    break;
                case XLLP_STUART:
                    ddi.dwIrq=IRQ_STUART;
                    break;
                default:
                    RETAILMSG(1,(TEXT("[UART]: Failed in wrong m_UART_Handle.UARTSelection.\r\n")));
                    goto FAIT_TO_INIT;
            }

            if (! KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &ddi.dwIrq, sizeof(UINT32), &ddi.dwSysintr, sizeof(UINT32), NULL)) 
            {
                RETAILMSG(1,(TEXT("[UART]: Get SYSINTR failed \r\n")));
                goto FAIT_TO_INIT;
            }

        }

        m_dwSysIntr = ddi.dwSysintr;

        if ((SYSINTR_UNDEFINED == m_dwSysIntr) || (0 == m_dwSysIntr))
        {
            RETAILMSG(1,(TEXT("[UART]: Init failed in wrong m_dwSysIntr.\r\n")));
            goto FAIT_TO_INIT;
        }

        m_hISTEvent = CreateEvent(0, FALSE, FALSE, NULL);

        if (NULL == m_hISTEvent)
        {
            RETAILMSG(1,(TEXT("[UART]: Init failed in wrong m_hISTEvent.\r\n")));
            goto FAIT_TO_INIT;
        }

        if (!InterruptInitialize(m_dwSysIntr, m_hISTEvent, 0, 0))
        {
            RETAILMSG(1,(TEXT("[UART]: Init failed in InterruptInitialize.\r\n")));
            m_dwSysIntr = SYSINTR_UNDEFINED ;
            goto FAIT_TO_INIT;
        }

        RETAILMSG(1, (TEXT("[UART] %d: interrupt initialize successfully IRQ=0x%x, SYSINTR =0x%x \r\n"),m_UART_Handle.UARTSelection, ddi.dwIrq, m_dwSysIntr));

        if (!MapHardware() || !CreateHardwareAccess())
        {
            RETAILMSG(1,(TEXT("[UART]: hardware initialize failed\r\n")));
            goto FAIT_TO_INIT;
        }
    }

    if (m_DMAEnabled)
    {
        if (!(m_DMAEnabled = DMAInit()))
        {
            RETAILMSG(1,(TEXT("[UART]: DMAInit initialize failed\r\n")));
            goto FAIT_TO_INIT;
        }
    }

    RETAILMSG(UART_DEBUG,(_T(" m_AutoFlowControlEnabled = %d, m_DMAEnabled = %d, m_UART_Handle.UARTSelection = %d\n"),
        m_AutoFlowControlEnabled, m_DMAEnabled, m_UART_Handle.UARTSelection));

    RETAILMSG(UART_DEBUG,(_T(" m_32BitBusEnabled = %d, m_fIRConnected = %d, m_dwWaterMark = %d\n"),
        m_32BitBusEnabled, m_fIRConnected, m_dwWaterMark));
    
    RETAILMSG(UART_DEBUG, (TEXT("[UART] Init Completed(++)\r\n")));
    return TRUE;

FAIT_TO_INIT:
    RETAILMSG(1,(TEXT("[UART]Fail to init \r\n")));
    return FALSE;
}

BOOL CPXAPdd16550::DMAInit()
{

    switch(m_UART_Handle.UARTSelection) {
        case XLLP_FFUART:
            m_cmrRxType = XLLP_DMAC_FFUART_RX;
            m_DMARxFIFO_physical = MONAHANS_BASE_REG_PA_FFUART;
            m_cmrTxType = XLLP_DMAC_FFUART_TX;
            m_DMATxFIFO_physical = MONAHANS_BASE_REG_PA_FFUART;
            break;
        case XLLP_BTUART:
            m_cmrRxType = XLLP_DMAC_BTUART_RX;
            m_DMARxFIFO_physical = MONAHANS_BASE_REG_PA_BTUART;
            m_cmrTxType = XLLP_DMAC_BTUART_TX;
            m_DMATxFIFO_physical = MONAHANS_BASE_REG_PA_BTUART;
            break;
        case XLLP_STUART:
            m_cmrRxType = XLLP_DMAC_STUART_RX;
            m_DMARxFIFO_physical = MONAHANS_BASE_REG_PA_SIRUART;
            m_cmrTxType = XLLP_DMAC_STUART_TX;
            m_DMATxFIFO_physical = MONAHANS_BASE_REG_PA_SIRUART;
            break;
        default:
            RETAILMSG(1, (TEXT("[UART]Invalid UART type\r\n"))); 
            return FALSE;
    }

    RETAILMSG(1, (TEXT("[UART] UART type=%d\r\n"),m_UART_Handle.UARTSelection)); 
    RETAILMSG(UART_DEBUG, (TEXT("[UART]m_cmrRxType = x%x\r\n"),m_cmrRxType)); 
    RETAILMSG(UART_DEBUG, (TEXT("[UART]m_DMARxFIFO_physical = x%x\r\n"),m_DMARxFIFO_physical)); 
    RETAILMSG(UART_DEBUG, (TEXT("[UART]m_cmrTxType =x%x\r\n"),m_cmrTxType )); 
    RETAILMSG(UART_DEBUG, (TEXT("[UART]m_DMATxFIFO_physical = x%x\r\n"),m_DMATxFIFO_physical)); 
    return ( DMARXInit() && DMATXInit() );
}

BOOL CPXAPdd16550::DMARXInit()
{

    _TCHAR eventName[20];
    //  Initialize DMA engine
    m_DMAC_RX_Handle = GdeDmacInit();
    if (m_DMAC_RX_Handle==NULL) {
        RETAILMSG(UART_DEBUG,(_T("[UART]Fail to initialize DMA RX engine \r\n")));
        return FALSE;
    }

    m_DmaAdapter.ObjectSize    = sizeof(DMA_ADAPTER_OBJECT);
    m_DmaAdapter.InterfaceType = Internal;
    m_DmaAdapter.BusNumber     = 0;

    // Allocate memory for DMA RX buffer and descriptor
    m_DMARxDescriptor = (BYTE *)HalAllocateCommonBuffer(&m_DmaAdapter, 
        (UART_DMA_RX_BUFFER_SIZE + sizeof(XLLP_DMAC_DESCRIPTOR_T)),
        &m_PA, FALSE);
    if (!m_DMARxDescriptor) {
        RETAILMSG(UART_DEBUG,(TEXT("[UART] Failt to create DMA Rx buffer\r\n")));
        return FALSE;
    }
    
    // Request DMA chanel
    if (XLLP_STATUS_SUCCESS != GdeDmacAllocChannel 
        (&m_DMARxChannel,  XLLP_DMAC_CHANNEL_PRIORITY_LOW)) {
        RETAILMSG(UART_DEBUG, (TEXT("[UART] Fail to allocate Rx channel\r\n")));
        return FALSE;
    }
    RETAILMSG(1,(TEXT("[UART] DMAInit: allocated Rx channel: [%d], descr:0x%x, phy=0x%x\r\n"),m_DMARxChannel,m_DMARxDescriptor,m_PA));
    
    // get the chanel event handler for DMA transfer
    // Compose the name of the channel event for use with CreateEvent(...) below.
    _stprintf(eventName, TEXT("DMA_CHANNEL_%d"), m_DMARxChannel);
    m_DMARxEvent = CreateEvent(NULL,FALSE,FALSE, eventName);
    if ((m_DMARxEvent == NULL) || (ERROR_ALREADY_EXISTS != GetLastError()))
    {
        RETAILMSG(UART_DEBUG,(TEXT("[UART] Failt to create Rx event[%s]\r\n"), &eventName));
        // Release DMA channel
        GdeDmacFreeChannel(m_DMARxChannel, m_cmrRxType);
        m_DMARxChannel = (XLLP_DMAC_CHANNEL_T) 0xFFFFFFFF;
        return FALSE;  
    }
    RETAILMSG(UART_DEBUG,(TEXT("[UART]DMAInit: created Rx event[%s]\r\n"), &eventName));
    
    // Set DRCMRx
    // No need to call this function as It's included in 

    m_DMARxBuffer = m_DMARxDescriptor + sizeof(XLLP_DMAC_DESCRIPTOR_T) ; 
    RETAILMSG(UART_DEBUG,(TEXT("[UART]m_DMARxBuffer  = 0x%X  ...\r\n"), m_DMARxBuffer));

    m_DMARxDescriptor_phy = (BYTE *)m_PA.LowPart;    
    m_DMARxBuffer_phy = m_DMARxDescriptor_phy + sizeof(XLLP_DMAC_DESCRIPTOR_T) ; 
    RETAILMSG(UART_DEBUG,(TEXT("[UART]m_DMARxBuffer_phy  = 0x%X  ...\r\n"), m_DMARxBuffer_phy));

    memset(&RxCommand, 0 , sizeof(XLLP_DMAC_COMMAND_T));
    RxCommand.aLen   =  UART_DMA_RX_BUFFER_SIZE;// Prepare for DMA transfer

    if (m_32BitBusEnabled)
    {
        RxCommand.aWidth      = XLLP_DMAC_WIDTH_32;            // Width: 4 bytes
    } else {
        RxCommand.aWidth      = XLLP_DMAC_WIDTH_8;            // Width: 1 byte
    }
    RxCommand.aSize       = XLLP_DMAC_BURSTSIZE_32;         //BurtSize: 32 bytes
    RxCommand.aEndian     = XLLP_FALSE;        // Little Endian.
    RxCommand.aEndIrqEn   = XLLP_TRUE;         // Interrupt when len = 0.
    RxCommand.aStartIrqEn = XLLP_FALSE;        // No Interrupt when Desc is loaded.
    RxCommand.aFlowTrg    = XLLP_FALSE;        // Target is DMA Rx buffer in memory
    RxCommand.aFlowSrc    = XLLP_TRUE;         // Source is  RxFIFO
    RxCommand.aIncTrgAddr = XLLP_TRUE;         // target address is incremental
    RxCommand.aIncSrcAddr = XLLP_FALSE;

    XllpDmacCfgChannelNoDescTransfer(
        m_DMAC_RX_Handle, 
        m_DMARxFIFO_physical,
        (XLLP_UINT32_T) m_DMARxBuffer_phy,
        &RxCommand,
        m_DMARxChannel,
        m_cmrRxType);

    // DMA  Rx is enabled
    return TRUE;
}

BOOL CPXAPdd16550::DMATXInit()
{
     _TCHAR eventName[20];

    //  Initialize DMA engine
    m_DMAC_TX_Handle = GdeDmacInit();
    if (m_DMAC_TX_Handle==NULL) {
        RETAILMSG(UART_DEBUG,(_T("[UART]Fail to initialize DMA TX engine \r\n")));
        return FALSE;
    }

    m_DmaAdapter.ObjectSize    = sizeof(DMA_ADAPTER_OBJECT);
    m_DmaAdapter.InterfaceType = Internal;
    m_DmaAdapter.BusNumber     = 0;

    // Allocate memory for DMA TX buffer and descriptor
    m_DMATxDescriptor = (BYTE *)HalAllocateCommonBuffer(&m_DmaAdapter, 
        (UART_DMA_TX_BUFFER_SIZE + sizeof(XLLP_DMAC_DESCRIPTOR_T)),
        &m_PA, FALSE);

    if (!m_DMATxDescriptor) {
        RETAILMSG(UART_DEBUG,(TEXT("[UART] Failt to create DMA Tx buffer\r\n")));
        return FALSE;
    }

    // Request DMA chanel
    if (XLLP_STATUS_SUCCESS != GdeDmacAllocChannel 
        (&m_DMATxChannel,  XLLP_DMAC_CHANNEL_PRIORITY_LOW)) {
        RETAILMSG(UART_DEBUG, (TEXT("[UART] Fail to allocate Tx channel\r\n")));
        return FALSE;
    }
    
    RETAILMSG(1,(TEXT("[UART] DMAInit: allocated Tx channel: [%d], descr:0x%x, phy=0x%x\r\n"),
              m_DMATxChannel,m_DMATxDescriptor,m_PA));
        
    // get the chanel event handler for DMA transfer
    // Compose the name of the channel event for use with CreateEvent(...) below.
    _stprintf(eventName, TEXT("DMA_CHANNEL_%d"), m_DMATxChannel);

    m_DMATxEvent = CreateEvent(NULL, FALSE, FALSE, eventName);

    if ((m_DMATxEvent == NULL) || (ERROR_ALREADY_EXISTS != GetLastError()))
    {
        RETAILMSG(UART_DEBUG,(TEXT("[UART] Failt to create Tx event[%s]\r\n"), &eventName));
        // Release DMA channel
        GdeDmacFreeChannel(m_DMATxChannel, m_cmrTxType);
        m_DMATxChannel = (XLLP_DMAC_CHANNEL_T) 0xFFFFFFFF;
        return FALSE;  
    }
    
    RETAILMSG(UART_DEBUG,(TEXT("[UART]DMAInit: created Tx event[%s]\r\n"), &eventName));

    m_DMATxBuffer = m_DMATxDescriptor + sizeof(XLLP_DMAC_DESCRIPTOR_T) ; 
    RETAILMSG(UART_DEBUG,(TEXT("[UART]m_DMATxBuffer = 0x%x\r\n"), m_DMATxBuffer ));
        
    m_DMATxDescriptor_phy = (BYTE *)m_PA.LowPart;    
    m_DMATxBuffer_phy = m_DMATxDescriptor_phy + sizeof(XLLP_DMAC_DESCRIPTOR_T) ; 
    RETAILMSG(UART_DEBUG,(TEXT("[UART]m_DMATxBuffer_phy  = 0x%x\r\n"), m_DMATxBuffer_phy));

    memset(&TxCommand, 0 , sizeof(XLLP_DMAC_COMMAND_T));
    TxCommand.aLen =  0;                                 // Prepare for DMA transfer
    if (m_32BitBusEnabled) {
        TxCommand.aWidth = XLLP_DMAC_WIDTH_32;          // Width: 4 byte
    } else {
        TxCommand.aWidth = XLLP_DMAC_WIDTH_8;           // Width: 1 byte
    }

    TxCommand.aSize = XLLP_DMAC_BURSTSIZE_32;         //BurtSize: 32 bytes
    TxCommand.aEndian = XLLP_FALSE;                   // Little Endian.
    TxCommand.aEndIrqEn = XLLP_TRUE;                  // Interrupt when len = 0.
    TxCommand.aStartIrqEn = XLLP_FALSE;               // No Interrupt when Desc is loaded.
    TxCommand.aFlowTrg = XLLP_TRUE;                   // Target is  TxFIFO
    TxCommand.aFlowSrc = XLLP_FALSE;                  // Src is DMA Tx buffer in memory
    TxCommand.aIncTrgAddr = XLLP_FALSE;
    TxCommand.aIncSrcAddr = XLLP_TRUE;                // Source address is incremental

    XllpDmacCfgChannelNoDescTransfer(m_DMAC_TX_Handle, 
                                     (XLLP_UINT32_T) m_DMATxBuffer_phy,
                                     m_DMATxFIFO_physical,
                                     &TxCommand,
                                     m_DMATxChannel,
                                     m_cmrTxType);

    // DMA  Tx is enabled
    return TRUE;
}


void  CPXAPdd16550::DMADeInit()
{
    // Disable IER[DMAE]
    GetRegister()->Write_IER(GetRegister()->Read_IER() & ~(PXA_SERIAL_IER_DMAE));

    // Release TX hanle
    m_DMAC_TX_Handle = NULL;

    // Release DMA Tx buffer
    m_DmaAdapter.ObjectSize    = sizeof(DMA_ADAPTER_OBJECT);
    m_DmaAdapter.InterfaceType = Internal;
    m_DmaAdapter.BusNumber     = 0;
    
    // The only required parameter is VirtualAddress,
    // which points to the allocated buffer.
    m_PA.LowPart = (DWORD) m_DMATxDescriptor_phy;
    m_PA.HighPart = 0;   
    HalFreeCommonBuffer(&m_DmaAdapter, 
        (UART_DMA_TX_BUFFER_SIZE + sizeof(XLLP_DMAC_DESCRIPTOR_T)), m_PA,
        (void *) m_DMATxDescriptor, 0);
    m_DMATxDescriptor = NULL;

    // Release DMA Tx event handle
    ResetEvent(m_DMATxEvent);
    CloseHandle(m_DMATxEvent);
    m_DMATxEvent = NULL;

    // Release DMA Tx channel
    GdeDmacFreeChannel(m_DMATxChannel, m_cmrTxType);
    m_DMATxChannel = (XLLP_DMAC_CHANNEL_T) 0xFFFFFFFF;

    // Release RX hanle
    m_DMAC_RX_Handle = NULL;

    // Release DMA Rx buffer
    m_DmaAdapter.ObjectSize    = sizeof(DMA_ADAPTER_OBJECT);
    m_DmaAdapter.InterfaceType = Internal;
    m_DmaAdapter.BusNumber     = 0;

    // The only required parameter is VirtualAddress,
    // which points to the allocated buffer.
    m_PA.LowPart = (DWORD) m_DMARxDescriptor_phy;
    m_PA.HighPart = 0;   
    HalFreeCommonBuffer(&m_DmaAdapter, 
        (UART_DMA_RX_BUFFER_SIZE + sizeof(XLLP_DMAC_DESCRIPTOR_T)), m_PA,
        (void *) m_DMARxDescriptor, 0);
    m_DMARxDescriptor = NULL;

    // Release DMA Rx event handle
    ResetEvent(m_DMARxEvent);
    CloseHandle(m_DMARxEvent);
    m_DMARxEvent = NULL;

    // Release DMA Rx channel
    GdeDmacFreeChannel(m_DMARxChannel, m_cmrRxType);
    m_DMARxChannel = (XLLP_DMAC_CHANNEL_T) 0xFFFFFFFF;

    RETAILMSG(UART_DEBUG, (TEXT("[UART]DMADeInit() is done. \r\n")));     
}

BOOL CPXAPdd16550::DMAReInitRXChannel()
{
    XllpDmacCfgChannelNoDescTransfer(m_DMAC_RX_Handle, 
                                     m_DMARxFIFO_physical,
                                     (XLLP_UINT32_T)m_DMARxBuffer_phy,
                                     &RxCommand,
                                     m_DMARxChannel,
                                     m_cmrRxType);

    m_DMAC_RX_Handle->dcsr[m_DMARxChannel] |= (XLLP_DMAC_DCSR_STOP_IRQ_EN
                                              |XLLP_DMAC_DCSR_EOR_STOP_EN
                                              );

    XllpDmacStartTransfer(m_DMAC_RX_Handle, m_DMARxChannel);

    RETAILMSG(UART_DEBUG,(TEXT("[UART] DMAReInitRXChannel\r\n")));
    return TRUE;
}

BOOL CPXAPdd16550::InitialEnableInterrupt(BOOL bEnable )
{
    if (m_DMAEnabled){
        m_HardwareLock.Lock();
        CSerialPDD::InitialEnableInterrupt(bEnable );

        if (bEnable) 
            m_pReg16550->Write_IER(PXA_SERIAL_IER_DMAE
                                   |SERIAL_IER_RLS 
                                   |SERIAL_IER_MS);
        else
            m_pReg16550->Write_IER(SERIAL_IER_MS);

        m_HardwareLock.Unlock();
        return TRUE;
    }
    else
    {
        return CPdd16550::InitialEnableInterrupt(bEnable);
    }
}

BOOL CPXAPdd16550::InitModem(BOOL bInit)
{

    CPdd16550::InitModem(bInit);

    RETAILMSG(UART_DEBUG, (TEXT("[UART] m_AutoFlowControlEnabled = %d .\r\n"), m_AutoFlowControlEnabled));
        
    if (bInit)
    {
        switch (m_AutoFlowControlEnabled)
        {
            case 1:
                //half auto-flow, automating only nCTS
                m_pReg16550->Write_MCR(m_pReg16550->Read_MCR()|SERIAL_MCR_AFE );
                RETAILMSG(1,(TEXT("[UART]UART type= %d, set half auto-flow\r\n"),m_UART_Handle.UARTSelection));
                break;
            case 2:
                //full auto-flow, automating both nCTS and nRTS
                m_pReg16550->Write_MCR(m_pReg16550->Read_MCR()|SERIAL_MCR_AFE|SERIAL_MCR_RTS );
                RETAILMSG(1,(TEXT("[UART]UART type= %d, set full auto-flow\r\n"),m_UART_Handle.UARTSelection));
                break;
            case 0:
            default:
                //no auto-flow
                RETAILMSG(UART_DEBUG,(TEXT("[UART]UART type= %d, set no auto-flow\r\n"),m_UART_Handle.UARTSelection));
        }
    }
    return TRUE;
}

BOOL CPXAPdd16550::InitReceive(BOOL bInit)
{
    if (m_DMAEnabled){

        m_HardwareLock.Lock();    

        if (bInit)
        {         
            BYTE uWarterMarkBit = GetWaterMarkBit();
            m_pReg16550->Write_FCR(m_pReg16550->Read_FCR()
            |SERIAL_FCR_RCVR_RESET 
            |SERIAL_FCR_ENABLE 
            |SERIAL_FCR_TRAILING_BYTES 
            | (uWarterMarkBit & SERIAL_IIR_FIFOS_ENABLED ));        
            m_pReg16550->Write_IER(m_pReg16550->Read_IER() 
            |PXA_SERIAL_IER_DMAE
            );
            m_pReg16550->Read_LSR(); // Clean Line Interrupt.
        }

        m_HardwareLock.Unlock();
        return TRUE;
        
    }else
        return CPdd16550::InitReceive(bInit);
}

BOOL    CPXAPdd16550::InitXmit(BOOL bInit)
{
    if (m_DMAEnabled)
    {
        m_HardwareLock.Lock();    

        if (bInit)
        { 
            m_pReg16550->Write_FCR(m_pReg16550->Read_FCR() 
             |SERIAL_FCR_TXMT_RESET 
             |SERIAL_FCR_ENABLE);
            m_pReg16550->Write_IER(m_pReg16550->Read_IER() 
            |PXA_SERIAL_IER_DMAE
            );
            m_XmitFifoEnable = TRUE;
        }
        
        m_HardwareLock.Unlock();
        return TRUE;
        
    }else
        return CPdd16550::InitXmit(bInit);
}

void CPXAPdd16550::PostInit()
{
    // We have to initial FCR because it got bus specific content inside.
    m_pReg16550->Write_FCR(0);
    if (m_32BitBusEnabled) {
        m_pReg16550->Write_FCR(GetRegister()->Read_FCR()|SERIAL_FCR_PERIPHERAL_BUS);    
    }
    GetRegister()->Write_ABR(0);
    CPdd16550::PostInit();
    return;
}

DWORD StartReceiveDMA(LPVOID lpParameter)
{
    RETAILMSG(UART_DEBUG,(TEXT("[UART]Start DMA receive thread\r\n")));
    CPXAPdd16550* pCPXAPdd16550 = (CPXAPdd16550*)lpParameter;
    pCPXAPdd16550->DMA_ReceiveHandler();
    return 1;
}


DWORD CPXAPdd16550::ThreadRun()
{
    if (m_DMAEnabled)
    {
        InitReceive(TRUE);
        if (!CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)StartReceiveDMA,this,0,NULL)){
            NKDbgPrintfW(TEXT("[UART] Error creating DMA receive thread (%d)\r\n"),GetLastError());
        }
    }

    while ( m_hISTEvent!=NULL && !IsTerminated())
    {
        if (WaitForSingleObject(m_hISTEvent,INFINITE)==WAIT_OBJECT_0)
        {
            UCHAR bData;
            
            while (!IsTerminated() && ((bData = m_pReg16550->Read_IIR()) & SERIAL_IIR_INT_INVALID)==0) 
            {
                RETAILMSG(UART_DEBUG,(TEXT("ThreadRun IIR=%x\r\n"),bData));
                DWORD interrupts=0;
                m_LineStatus = GetLineStatus();
                
                switch ( bData & SERIAL_IIR_INT_MASK )
                {

                    case SERIAL_IIR_RLS:
                    {
                        interrupts |= INTR_LINE;
                        RETAILMSG(UART_DEBUG,(TEXT("[UART]Line interrupt\r\n")));

                        //workaround for TX interrupt missing when full duplex  (only for FFUART)
                        //Tibet ID: 1981078
                        //DMA mode is not affected by this sighting.
                        #ifdef TX_INTE_MISSING_WHEN_FULL_DUPLEX
                        if ((!m_DMAEnabled) && (m_UART_Handle.UARTSelection == XLLP_FFUART))
                        {
                             if (m_LineStatus & SERIAL_LSR_THRE)
                             {
                                interrupts |= INTR_TX; 
                                RETAILMSG(1,(_T("[UART] Missing the TX INT when line interrupt.\r\n ")));
                             }
                        }
                        #endif

                        break;
                    }
 
                    case SERIAL_IIR_CTI: case SERIAL_IIR_CTI_2: case SERIAL_IIR_RDA:
                    {
                        interrupts |= INTR_RX;

                        #ifdef TX_INTE_MISSING_WHEN_FULL_DUPLEX 
                        if ((!m_DMAEnabled) && (m_UART_Handle.UARTSelection == XLLP_FFUART) )
                        {
                            if (m_LineStatus & SERIAL_LSR_THRE)
                            {
                                interrupts |= INTR_TX; 
                                RETAILMSG(UART_DEBUG,(_T("[UART] Missing the TX INT when RX interrupt.\r\n ")));
                            }
                        }
                        #endif

                        break;
                    }

                    case SERIAL_IIR_THRE:
                    {
                        interrupts |= INTR_TX; 
                        RETAILMSG(UART_DEBUG,(TEXT("[UART]TX interrupt\r\n")));
                        break;
                    }
                    
                    case SERIAL_IIR_MS :
                    {
                        interrupts |= INTR_MODEM;
                        RETAILMSG(UART_DEBUG,(TEXT("[UART]Modem interrupt\r\n")));
                        break;
                    }

                    default:
                        ASSERT(FALSE);
                }

                NotifyPDDInterrupt((INTERRUPT_TYPE)interrupts);
            }
            
            if (m_dwSysIntr!= SYSINTR_UNDEFINED) InterruptDone(m_dwSysIntr);
        }
        else
            ASSERT(FALSE);
    }

    return 1;
}

BOOL  CPXAPdd16550::Open()
{
    BOOL bReturn =  CPdd16550::Open();
    
    if (bReturn)
    {        
        // Need Enable UART.
        m_HardwareLock.Lock();   

        if (m_DMAEnabled)
            GetRegister()->Write_IER(GetRegister()->Read_IER() | PXA_SERIAL_IER_UUE);
        else
            GetRegister()->Write_IER(GetRegister()->Read_IER() | PXA_SERIAL_IER_UUE | PXA_SERIAL_IER_RTOIE);

        InitIR(TRUE);
        m_HardwareLock.Unlock();
    }

    return bReturn;
}
BOOL  CPXAPdd16550::Close()
{
    BOOL bReturn =  CPdd16550::Close();
    
    if (bReturn)
    { 
        // Need Disable UART.
        m_HardwareLock.Lock();   
        InitIR(FALSE);

        if (m_DMAEnabled)
             GetRegister()->Write_IER(GetRegister()->Read_IER() &(~( PXA_SERIAL_IER_UUE)));
        else
             GetRegister()->Write_IER(GetRegister()->Read_IER() &~( PXA_SERIAL_IER_UUE|PXA_SERIAL_IER_RTOIE));

        m_HardwareLock.Unlock();
    }
    
    return bReturn;
}

BOOL CPXAPdd16550::MapHardware()
{
    BOOL bFirstInit = FALSE;
    
    if (!GetRegValue(PC_REG_REGSTRIDE_VAL_NAME,(PBYTE)&m_dwRegStride, PC_REG_REGSTRIDE_VAL_LEN)) 
    {
        m_dwRegStride = 4;// Default is 4 for PXA
    }
    // Get IO Window From Registry
    if (m_pBaseAddress == NULL ) 
    {
        DDKWINDOWINFO dwi;
        if ( GetWindowInfo( &dwi)==ERROR_SUCCESS &&
                dwi.dwNumMemWindows >= 1 &&  dwi.memWindows[0].dwBase != 0 && dwi.memWindows[0].dwLen >=  m_dwRegStride * 0x10)  
        {
            PHYSICAL_ADDRESS ioPhysicalBase = { dwi.memWindows[0].dwBase, 0};
            m_pBaseAddress = (PPXA_UART_REG)MmMapIoSpace(ioPhysicalBase, dwi.memWindows[0].dwLen,FALSE);
        }
    }
    if (m_pGPIOReg == NULL) 
    {
        PHYSICAL_ADDRESS ioPhysicalBase = {MONAHANS_BASE_REG_PA_GPIO,0};
        m_pGPIOReg = (PPXA_GPIO_REG)MmMapIoSpace(ioPhysicalBase, sizeof(PXA_GPIO_REG),FALSE);
    }
    if (m_pDCCLKReg == NULL ) 
    {
        PHYSICAL_ADDRESS ioPhysicalBase = {MONAHANS_BASE_REG_PA_CLKMGR, 0 };
        m_pDCCLKReg = (PPXA_CLKMGR_REG)MmMapIoSpace(ioPhysicalBase, sizeof(PXA_CLKMGR_REG),FALSE);
    }    
    
    if (m_pMFPReg == NULL ) 
    {
        PHYSICAL_ADDRESS ioPhysicalBase = {MONAHANS_BASE_REG_PA_MFP, 0 };
        m_pMFPReg = (unsigned int    *)MmMapIoSpace(ioPhysicalBase, 0x700,FALSE);
    }    

    if (m_pOSTRegs == NULL ) 
    {
        PHYSICAL_ADDRESS ioPhysicalBase = {MONAHANS_BASE_REG_PA_OST, 0 };
	    m_pOSTRegs  = (P_XLLP_OST_T) MmMapIoSpace(ioPhysicalBase, sizeof(XLLP_OST_T), FALSE);
    }    

    // Claim memory-mapped file for sharing MfpRmDb information.
    m_MfpRMDbHandle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
            0, XLLP_MFP_RM_DB_SIZE, MFPRMDB_SHARED_FILENAME );

    if (NULL == m_MfpRMDbHandle)        
    {
        NKDbgPrintfW(TEXT("[UART] Fail to CreateFileMapping(MFPRMDB_SHARED_FILENAME)! \r\n"));
        return FALSE; //if failed
    } 
    else 
    {
        // We need to tell if it's first or sequent call of  CreateFileMapping(MFPRMDB_SHARED_FILENAME)
        if (ERROR_ALREADY_EXISTS != GetLastError())
        {
            // The first initialization of this memory-mapped file.
            RETAILMSG(UART_DEBUG, (TEXT("[UART] First call of CreateFileMapping(MFPRMDB_SHARED_FILENAME)! \r\n")));     
            bFirstInit = TRUE;      
        } 
        else 
        {
            // Memory-mapped file already existed.
            RETAILMSG(UART_DEBUG, (TEXT("[UART] Sequent call of CreateFileMapping(MFPRMDB_SHARED_FILENAME)! \r\n")));
            bFirstInit = FALSE;
        }
 
        // Retrieve the local handle of the global shared memory-mapped file
        m_pMfpRMDb = (P_XLLP_MFP_RM_DB_ID_T) MapViewOfFile(m_MfpRMDbHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0 );
        if (NULL == m_pMfpRMDb )
        {
            NKDbgPrintfW(TEXT("[UART] Fail to MapViewOfFile()!\r\n"));
            return FALSE; //if failed
        }

        if (TRUE == bFirstInit) 
        {
            // Need to Initialize RM    
            if (XLLP_STATUS_SUCCESS != XllpMfpResourceManagerInit(m_pMfpRMDb)) 
            {
                NKDbgPrintfW(TEXT("[UART] Fail to XllpMfpResourceManagerInit()!\r\n"));
                  return FALSE; //if failed
            }
        }
    }// end if (NULL == m_MfpRMDbHandle)

    RETAILMSG(UART_DEBUG,(TEXT("CPXAPdd16550::MapHardware: m_pBaseAddress:%x m_pGPIOReg:%x m_pDCCLKReg:%x m_pMFPReg:%x\r\n"),
        m_pBaseAddress,m_pGPIOReg,m_pDCCLKReg,m_pMFPReg));
    
    return(m_pBaseAddress!=NULL && m_pGPIOReg!=NULL && m_pDCCLKReg!=NULL && m_pMFPReg!=NULL); 
}
BOOL CPXAPdd16550::CreateHardwareAccess()
{
    if (m_pReg16550)
        return TRUE;
    if (m_pBaseAddress!=NULL) 
    {
        m_pReg16550 = new CPXAReg16550(m_pBaseAddress);
        if (m_pReg16550 && !m_pReg16550->Init()) 
        { // FALSE.
            delete m_pReg16550 ;
            m_pReg16550 = NULL;
        }
    }
    return (m_pReg16550!=NULL);
}

BYTE  CPXAPdd16550::GetWaterMarkBit()
{
    return CPdd16550::GetWaterMarkBit();
}

DWORD CPXAPdd16550::GetWaterMark()
{
    return CPdd16550::GetWaterMark();
}

BOOL CPXAPdd16550::GetDivisorOfRate(ULONG BaudRate,PULONG pulDivisor)
{
    static const
    PAIRS  s_LS_BaudPairs[] = {
        {50,        18432},
        {75,        12288},
        {150,       6144},
        {300,       3072},
        {600,       1536},
        {1200,      768},
        {1800,      512},
        {2400,      384},
        {3600,      256},
        {4800,      192},
        {7200,      128},
        {9600,      96},
        {12800,     72},
        {14400,     64},
        {19200,     48},
        {23040,     40},
        {28800,     32},
        {38400,     24},
        {57600,     16},
        {115200,    8},
        {230400,    4},
        {921600,    1},
        {1842000,  2},
        {3686400,  1},
    };

    for (DWORD dwIndex =0 ; dwIndex <dim(s_LS_BaudPairs) && s_LS_BaudPairs[dwIndex].Key<=BaudRate; dwIndex ++) {
        if (s_LS_BaudPairs[dwIndex].Key== BaudRate){

    RETAILMSG(1,(_T("[UART]CPXAPdd16550::GetDivisorOfRate matched BaudRate = %d.\r\n"),BaudRate));

			
            if (pulDivisor)
                *pulDivisor = s_LS_BaudPairs[dwIndex].AssociatedValue;
            //NKDbgPrintfW(_T("[UART] Divisor=0x%x\r\n"),*pulDivisor);
            return TRUE;
        }
    }

    return FALSE;    
}

BOOL CPXAPdd16550::SetBaudRate(ULONG BaudRate,BOOL /*bIrModule*/)
{
    ULONG ulDivisor;

	DWORD dwIndex = 0;

    if (GetDivisorOfRate(BaudRate,&ulDivisor)) 
	{
        m_HardwareLock.Lock();	
		//To make sure the FIFO is completely empty before change baudrate.
	    for (dwIndex = 0; dwIndex < DELAY_FOR_FIFO_EMPTY_MAXVALUE; dwIndex++)
	    {
			if( (GetRegister()->Read_LSR( ) & SERIAL_LSR_TEMT) == 0)  
			{
				XllpOstDelayMicroSeconds((P_XLLP_OST_T) m_pOSTRegs,DELAY_FOR_FIFO_EMPTY_INTERVAL);
			}
	        else
	            break;
	    }

		if(dwIndex)
		{
		    RETAILMSG(1, (TEXT("CPXAPdd16550::SetBaudRate, the transmit holding buffer is NOT empty Or the hardware is NOT ready to accept another character to transmission before we change BaudRate. Delay %d ms \r\n"),(dwIndex*5)/1000));				
		}

        if (m_dwSysIntr!=SYSINTR_UNDEFINED)
            InterruptMask(m_dwSysIntr,TRUE);
        if (BaudRate==1842000 || BaudRate == 3686400)
        {
            GetRegister()->Write_IER32(GetRegister()->Read_IER32() | PXA_SERIAL_IER_HSE );
        }
        else
        {
            GetRegister()->Write_IER32(GetRegister()->Read_IER32() & (~PXA_SERIAL_IER_HSE));
        }
        __try {
            m_pReg16550->Write_BaudRate((UINT16)ulDivisor);
            }__except( EXCEPTION_EXECUTE_HANDLER ) {
        };
        if (m_dwSysIntr!=SYSINTR_UNDEFINED)
            InterruptMask(m_dwSysIntr,FALSE);
        m_HardwareLock.Unlock();      
        return TRUE;
    }
    else
        return FALSE;
}
BOOL CPXAPdd16550::Enable_IR_Rx_Tx(BOOL Rxenable, BOOL Txenable)
{
    XLLP_BOOL_T TxEnable, RxEnable; 	

    if (m_fIRConnected)
    {
        TxEnable = (Txenable)? XLLP_TRUE : XLLP_FALSE;
        RxEnable = (Rxenable)? XLLP_TRUE : XLLP_FALSE;

        // new way
        if (XLLP_STATUS_SUCCESS == XllpSIRTxRxEnable(&m_IRDA_Handle, TxEnable, RxEnable))
        {
            return TRUE;
        } 
        else
        {
            return FALSE;
        }
    }
    else
        return TRUE;
    //Restore to original value of IER including Uart Unit setting
}
BOOL CPXAPdd16550::InitIR(BOOL bSet)
{
    //Updating our copy of IER before disabling UART
    m_HardwareLock.Lock();        
    BYTE bIER = GetRegister()->Read_IER();

    //Disable the UART for sure
    GetRegister()->Write_IER(bIER & ~PXA_SERIAL_IER_UUE);
    if (m_fIRConnected && bSet) 
    {
        //Updating our copy of pIRDASEL before enabling/disabling IR
        BYTE uIRSel = GetRegister()->Read_SIR() & ~INFRARED_ISR_TX_DATA_POLARITY;
        uIRSel |=  (INFRARED_ISR_XMODE| INFRARED_ISR_RX_DATA_POLARITY);
        GetRegister()->Write_SIR( uIRSel );
        Enable_IR_Rx_Tx( TRUE, FALSE);

    }
    else {
        GetRegister()->Write_SIR(0);
    }

    //Restoring to the original IER (including UART unit state).
    GetRegister()->Write_IER(bIER);
    m_HardwareLock.Unlock();
    return m_fIRConnected;
    
}
void CPXAPdd16550::SetOutputMode(BOOL fUseIR, BOOL fUse9Pin)
{
    CPdd16550::SetOutputMode(fUseIR,fUse9Pin);
    if (m_fIREnable) {
        InitIR(TRUE);
        Enable_IR_Rx_Tx(TRUE,FALSE);
    }
    else
        InitIR(FALSE);
}
DWORD   CPXAPdd16550::GetWriteableSize()
{
    DWORD dwByteCanWrite = 0;
    BYTE bLSR = 0;
    
    if (m_DMAEnabled)
    {
        dwByteCanWrite = UART_DMA_TX_BUFFER_SIZE;
    }
    else
    {
        bLSR = GetRegister()->Read_LSR( );
        if (bLSR & SERIAL_LSR_TEMT) 
        {
            dwByteCanWrite = (m_XmitFifoEnable?PXA_SERIAL_FIFO_DEPTH:1);
        } 
        else if (bLSR & SERIAL_LSR_THRE) 
        {
           dwByteCanWrite = (m_XmitFifoEnable?(PXA_SERIAL_FIFO_DEPTH/2):1);
        }
    }
    
    return dwByteCanWrite;
}
BOOL  CPXAPdd16550::EnableXmitInterrupt(BOOL fEnable)
{
    RETAILMSG(UART_DEBUG, (TEXT("[UART] EnableXmitInterrupt(%d)\r\n"),fEnable));          
    if (m_fIREnable)
    {
        Enable_IR_Rx_Tx(!fEnable,fEnable);
    }
    return CPdd16550::EnableXmitInterrupt(fEnable);
}
void CPXAPdd16550::XmitInterruptHandler(PUCHAR pTxBuffer, ULONG *pBuffLen)
{

	DWORD dwIndex = 0;
    RETAILMSG(UART_DEBUG, (TEXT("[UART]XmitInterruptHandler\r\n")));	
    if (!(pTxBuffer!=NULL && pBuffLen!=NULL && *pBuffLen!=NULL))
    { 
	    // The Xmit Empty interrupt can be generated by two sources.
	    // One is Xmit FIFO half empty, other is empty.
	    // We have to make sure the FIFO is completely empty before return this.
	    // Because after this return, user may shut down hardware or change baudrate. 

	    // To make sure the FIFO is completely empty before change baudrate.
	    // If there is no transmission control here, most IrDA communication will fail,
	    // such as IrDA LTK 4017, IrDA connection with PC, Beam file between two phones via IrDA. Afther this, they can work well.
	    // Please be noted to use XllpOstDelayMicroSeconds instead of Sleep here.
	    for (dwIndex = 0; dwIndex < DELAY_FOR_FIFO_EMPTY_MAXVALUE; dwIndex++)
	    {
	        if ((GetRegister()->Read_LSR( ) & SERIAL_LSR_TEMT) == 0 ) 
	    	{
			    XllpOstDelayMicroSeconds((P_XLLP_OST_T) m_pOSTRegs, DELAY_FOR_FIFO_EMPTY_INTERVAL);
			}
	        else
	            break;
	    }

		if(dwIndex)
		{
			RETAILMSG(UART_DEBUG, (TEXT("%d "),(dwIndex*DELAY_FOR_FIFO_EMPTY_INTERVAL)/1000 ));				
		}
	}

		
    if (m_fIREnable)
    {
        if (pTxBuffer!=NULL && pBuffLen!=NULL && *pBuffLen!=NULL) 
            Enable_IR_Rx_Tx(FALSE,TRUE);
        else
            Enable_IR_Rx_Tx(TRUE,FALSE);
    }

    if (m_DMAEnabled)
    {
        DMA_XmitHandler(pTxBuffer,pBuffLen);
        RETAILMSG(UART_DEBUG, (TEXT("[UART] Number of bytes Tx= 0x%X.\r\n"),*pBuffLen));          
    }
    else
        PIO_XmitInterruptHandler(pTxBuffer, pBuffLen);
   
}

void    CPXAPdd16550::XmitComChar(UCHAR ComChar)
{
    if (m_fIREnable)
    {
        Enable_IR_Rx_Tx(FALSE,TRUE);
    }
    CPdd16550::XmitComChar(ComChar);
}

BOOL CPXAPdd16550::SetDCB(LPDCB lpDCB)
{
    m_HardwareLock.Lock();
    //Disable the UART for sure
    Enable_IR_Rx_Tx(FALSE,FALSE);
    BOOL bReturn = CPdd16550::SetDCB(lpDCB);
    Enable_IR_Rx_Tx(TRUE,FALSE);
    m_HardwareLock.Unlock();        
    return bReturn;
}

ULONG   CPXAPdd16550::ReceiveInterruptHandler(PUCHAR pRxBuffer,ULONG *pBufflen)
{
    ULONG ReturnValue;

    BYTE bIER = GetRegister()->Read_IER();
    GetRegister()->Write_IER( bIER & ~PXA_SERIAL_IER_RTOIE);


    //RETAILMSG(UART_DEBUG, (TEXT("[UART]RxInt in PIO mode.\r\n")));
    ReturnValue = PIO_ReceiveInterruptHandler(pRxBuffer, pBufflen);

    bIER = GetRegister()->Read_IER();
    GetRegister()->Write_IER( bIER | PXA_SERIAL_IER_RTOIE);

    return ReturnValue;

}

ULONG   CPXAPdd16550::GetModemStatus()
{
    m_HardwareLock.Lock();    
    ULONG ulReturn =0 ;
    ULONG Events = 0;
    UINT8 ubModemStatus = m_pReg16550->Read_MSR();
    RETAILMSG(UART_DEBUG, (TEXT("CPXAPdd16550: #### ubModemStatus = %x ####\r\n"), ubModemStatus));    
    m_HardwareLock.Unlock();

    // Event Notification.
    if (ubModemStatus & SERIAL_MSR_DCTS)
        Events |= EV_CTS;
    if ( ubModemStatus  & SERIAL_MSR_DDSR )
        Events |= EV_DSR;
    if ( ubModemStatus  & SERIAL_MSR_TERI )
        Events |= EV_RING;
    if ( ubModemStatus  & SERIAL_MSR_DDCD )
        Events |= EV_RLSD;

    // Report Modem Status;
    if ( ubModemStatus & SERIAL_MSR_CTS )
        ulReturn |= MS_CTS_ON;
    if ( ubModemStatus & SERIAL_MSR_DSR )
         ulReturn |= MS_DSR_ON;
    if ( ubModemStatus & SERIAL_MSR_RI )
         ulReturn  |= MS_RING_ON;
    if ( ubModemStatus & SERIAL_MSR_DCD )
         ulReturn  |= MS_RLSD_ON;

    if (Events!=0)
        EventCallback(Events,ulReturn);
     

    /* Check if it is use software flow control */
    if (2 != m_AutoFlowControlEnabled)
    {
        if (m_DMAEnabled)
        {
            /* For DMA Enabled, it need to stop DMA channel for flow control when needed */
            if ((m_DCB.fOutxCtsFlow && ((ulReturn & MS_CTS_ON) == 0)) 
            || (m_DCB.fOutxDsrFlow && ((ulReturn & MS_DSR_ON) == 0)))
            {
                // We are in flow off
                RETAILMSG(1, (TEXT("[UART]GetModemStatus(): Flow Off, Data Discard.\r\n")));
                //  Stop the Transfer to cause timeout
                m_DMAC_HardwareLock.Lock();
                XllpDmacStopTransfer(m_DMAC_TX_Handle, m_DMATxChannel);
                m_DMAC_HardwareLock.Unlock();
            }        
        }
        else
        {
            /* For DMA Not Enabled, it need to disable interrupt when needed */
            if (m_DCB.fOutxCtsFlow && (Events & EV_CTS))
            {
                if (ulReturn & MS_CTS_ON)
                    EnableXmitInterrupt(TRUE);        
                else
                    EnableXmitInterrupt(FALSE);        
            }
        }
    }
    else
    {
        /* It is hardware flow control, it needn't do nothing */
        RETAILMSG(UART_DEBUG, (TEXT("[UART]: CPXAPdd16550::GetModemStatus hardware flow handling\r\n")));    
    }
    
    return ulReturn;
}

BYTE CPXAPdd16550::GetLineStatus()
{
    return CPdd16550::GetLineStatus();
}


void  CPXAPdd16550::LineInterruptHandler() 
{
    RETAILMSG(UART_DEBUG,(TEXT("[UART]Line interrupt. UART type=%d. m_LineStatus =%x\r\n"), m_UART_Handle.UARTSelection, m_LineStatus));

    if (m_LineStatus&(SERIAL_LSR_FE|SERIAL_LSR_PE|SERIAL_LSR_OE))
    {
        m_HardwareLock.Lock();
        GetRegister()->Write_FCR(GetRegister()->Read_FCR() | SERIAL_FCR_RCVR_RESET);
        RETAILMSG(1, (TEXT("[UART]Line interrupt. SERIAL_FCR_RCVR_RESET Reset \r\n")));
        m_HardwareLock.Unlock();
    }
    return;
}

VOID CPXAPdd16550::DMA_ReceiveHandler()
{
    DWORD dwBytesDropped = 0;
    DWORD dwBytesStored = 0 ;
    ULONG dwRoomLeft = 0;
    DWORD DCSRValue;
    DWORD dwDataAvaiable;
    DWORD dwDataIndex;
    XLLP_UINT32_T val;

    while (m_DMARxEvent!=NULL)
    {
        XllpDmacCfgChannelNoDescTransfer(m_DMAC_RX_Handle, 
                                         m_DMARxFIFO_physical,
                                         (XLLP_UINT32_T) m_DMARxBuffer_phy,
                                         &RxCommand,
                                         m_DMARxChannel,
                                         m_cmrRxType);


         val = (m_DMAC_RX_Handle->dcsr[m_DMARxChannel]& XLLP_DCSR_WRITABLES_MSK) & (~XLLP_DMAC_DCSR_WRITE_ONE_TO_CLEAR_BITS);
         m_DMAC_RX_Handle->dcsr[m_DMARxChannel] = (val & (~XLLP_DMAC_DCSR_MASK_RUN)) | XLLP_DMAC_DCSR_RUN| XLLP_DMAC_DCSR_STOP_IRQ_EN |XLLP_DMAC_DCSR_EOR_STOP_EN;
 
        if (WaitForSingleObject(m_DMARxEvent, INFINITE) == WAIT_OBJECT_0)
        {
            UCHAR bData = m_pReg16550->Read_IIR();
            DCSRValue = GetEventData(m_DMARxEvent);
            RETAILMSG(UART_DEBUG, (TEXT("[UART]RX Event handler. DCSR=0x%X, IIR=0x%x\r\n"), DCSRValue,bData));
            RETAILMSG(UART_DEBUG, (TEXT("[UART]after transfer RX.DCSR[%d]  = 0x%X  ...\r\n"),  m_DMARxChannel, m_DMAC_RX_Handle->dcsr[m_DMARxChannel]));

            if (XLLP_DMAC_DCSR_STOP_INTR & DCSRValue)
            {
                dwDataAvaiable=RxCommand.aLen-(m_DMAC_RX_Handle->ddg[m_DMARxChannel].dcmd&XLLP_DCMD_LEN_MASK);
                RETAILMSG(UART_DEBUG, (TEXT("[UART]RX.DCMD [%d] = 0x%X  ...\r\n"), m_DMARxChannel, m_DMAC_RX_Handle->ddg[m_DMARxChannel].dcmd));
                RETAILMSG(UART_DEBUG, (TEXT("[UART] DMA Rx Received: 0x%X\r\n"),dwDataAvaiable)); 
                
                dwDataIndex = 0;
                if (dwDataAvaiable>0)
                {
                    while(dwDataAvaiable>0)
                    {
                        register DWORD RxWIndex=RxWrite(m_pMDDSerialHead), RxRIndex=RxRead(m_pMDDSerialHead);

                        if ( RxRIndex == 0 )
                        {
                            // have to leave one byte free.
                            dwRoomLeft = RxLength(m_pMDDSerialHead) - RxWIndex - 1;
                        }
                        else
                        {
                            dwRoomLeft = RxLength(m_pMDDSerialHead) - RxWIndex;
                        }

                        if ( RxRIndex > RxWIndex )
                        {
                            dwRoomLeft = RxRIndex - RxWIndex - 1;
                        }
                        
                        if ( dwRoomLeft )
                        {
                           dwBytesStored =(dwDataAvaiable<dwRoomLeft? dwDataAvaiable: dwRoomLeft);

                            __try
                            {
                                memcpy (RxBuffWrite(m_pMDDSerialHead), m_DMARxBuffer + dwDataIndex, dwBytesStored );
                                dwDataIndex += dwBytesStored;
                            }
                            __except(EXCEPTION_EXECUTE_HANDLER)
                            {
                                RETAILMSG(1, ( _T("[UART]DMA_ReceiveHandler memcpy exception!") ) );
                                ASSERT(FALSE);
                            }

                            
                            RETAILMSG(UART_DEBUG, (TEXT("[UART] Number of bytes Rx= 0x%X.\r\n"),dwBytesStored)); 
                            for (UINT i=0;i<dwBytesStored;i++) 
                                RETAILMSG(UART_DEBUG, (TEXT("[UART] DMA Rx BUffer: bytes[%d] = 0x%X\r\n"),i,*(m_DMARxBuffer+i))); 

                            dwDataAvaiable -=dwBytesStored;
                            RxWrite(m_pMDDSerialHead) = 
                                (RxWrite(m_pMDDSerialHead)+dwBytesStored<RxLength(m_pMDDSerialHead)?
                                RxWrite(m_pMDDSerialHead)+dwBytesStored: 
                                RxWrite(m_pMDDSerialHead)+dwBytesStored-RxLength(m_pMDDSerialHead));
                            RETAILMSG (UART_DEBUG,
                                (TEXT("[UART]Input Buffer(R=%d,W=%d,L=%d)\r\n"),
                                RxRead(m_pMDDSerialHead), RxWrite(m_pMDDSerialHead),
                                RxLength(m_pMDDSerialHead)));
                        }
                        else
                        {
                            // just delay  and try again.
					  	     XllpOstDelayMilliSeconds((P_XLLP_OST_T) m_pOSTRegs, 1);                            
                             RETAILMSG(1,(TEXT("[UART] no enough space in the input buffer. Wait for 1ms\r\n") ));
                        }
                    } //End of while
                    
                    RETAILMSG(UART_DEBUG,(_T("[UART] SetEvent  hReadEvent\r\n")));
                    SetEvent(m_pMDDSerialHead->hReadEvent);
                    RETAILMSG(UART_DEBUG,(_T("[UART] EvaluateEventFlag\r\n")));
                    EvaluateEventFlag(m_pMDDSerialHead, EV_RXCHAR);
                }  // End if 
            }
            else
            {
                RETAILMSG(UART_DEBUG,(_T("[UART] Not DMA STOP Interrupt.\r\n")));
            }
        }
    }
    return;
}

void CPXAPdd16550::DMA_XmitHandler(PUCHAR pTxBuffer, ULONG *pBuffLen)
{
    m_HardwareLock.Lock();    
    
    if(!pBuffLen)
    {
        RETAILMSG( 1, ( _T("[UART]DMA_XmitHandler ERROR, invalid parameter!") ) );
        return;
    }

    if (*pBuffLen == 0)
    {
        RETAILMSG(UART_DEBUG, (TEXT("[UART]DMAWriteBuffer(): length==0.\r\n")));
    }
    else
    {
        DEBUGCHK(pTxBuffer);
        PulseEvent(m_XmitFlushDone);

        DWORD dwDataAvaiable = *pBuffLen;
        DWORD dwDataSent = 0;
        BOOL bEverTimeout = FALSE;
        *pBuffLen = 0;
        COMMTIMEOUTS timeOuts = m_pMDDSerialHead->CommTimeouts;
     
        //if flow control disabled, need to control transmission speed.
        if(!(m_DCB.fOutxCtsFlow || m_DCB.fOutxDsrFlow))
        {
            for (DWORD dwIndex = 0; dwIndex< 500; dwIndex++) 
            {
                if ((GetRegister()->Read_LSR( ) & SERIAL_LSR_TEMT) == 0 ) 
                {
			  	     XllpOstDelayMilliSeconds((P_XLLP_OST_T) m_pOSTRegs, 1);         
                }
                else
                    break;
            }
        }
        
        //check for flow off status
        if ((m_DCB.fOutxCtsFlow && IsCTSOff()) ||(m_DCB.fOutxDsrFlow && IsDSROff())) 
        {
            m_HardwareLock.Unlock();
            return;
        }

        while (dwDataAvaiable > 0)
        {
            DWORD dwWriteSize = GetWriteableSize();
            if (dwWriteSize > dwDataAvaiable)
            {
                dwWriteSize = dwDataAvaiable;
            }

            __try
            {
                // AP's output buffer => DMA Tx buffer
                memcpy (m_DMATxBuffer , pTxBuffer, dwWriteSize);
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {
                RETAILMSG( UART_DEBUG, ( _T("DMA_XmitHandler Exception!") ) );
                ASSERT(FALSE);
            }

            TxCommand.aLen=((XLLP_INT16_T)dwWriteSize&XLLP_DCMD_LEN_MASK);

            XllpDmacCfgChannelNoDescTransfer(
                    m_DMAC_TX_Handle, 
                    (XLLP_UINT32_T) m_DMATxBuffer_phy,
                    m_DMATxFIFO_physical,
                    &TxCommand,
                    m_DMATxChannel,
                    m_cmrTxType);

            XllpDmacStartTransfer(m_DMAC_TX_Handle, m_DMATxChannel);

            RETAILMSG(UART_DEBUG,(TEXT("[UART]asasaDMAWriteBuffer(): submitting 0x%X bytes...\r\n"),  (XLLP_INT16_T)dwWriteSize));

            ULONG timeoutValue = timeOuts.WriteTotalTimeoutMultiplier*dwWriteSize + timeOuts.WriteTotalTimeoutConstant;

            if (!timeoutValue) timeoutValue = INFINITE;
            RETAILMSG(UART_DEBUG,(TEXT("[UART]DMAWriteBuffer(): timeoutValue= 0x%X bytes...\r\n"),  (XLLP_INT16_T)timeoutValue));

            if (WAIT_OBJECT_0 == WaitForSingleObject(m_DMATxEvent, timeoutValue)) 
            {                 
                // DMA transfer is completed
                dwDataAvaiable -= dwWriteSize; // remaining bytes
                dwDataSent += dwWriteSize; //sent bytes

                __try
                {
                     pTxBuffer += dwWriteSize; // point to next packet
                }
                 __except(EXCEPTION_EXECUTE_HANDLER)
                {
                     RETAILMSG( UART_DEBUG, ( _T("DMA_XmitHandler Exception!") ) );
                    ASSERT(FALSE);
                }
            }
            else
            {
                // Timeout
                RETAILMSG(UART_DEBUG,(TEXT("[UART]DMAWriteBuffer(): Fail to WaitForSingleObject\r\n")));
                // Stop the Transfer.
                XllpDmacStopTransfer(m_DMAC_TX_Handle, m_DMATxChannel);
                DWORD dwDCMD = m_DMAC_TX_Handle->ddg[m_DMATxChannel].dcmd;
                // number of partially completed bytes in current DMA transfer
                dwDataSent += (dwWriteSize - (dwDCMD & XLLP_DCMD_LEN_MASK));
                dwDataAvaiable = 0; // break out
                bEverTimeout = TRUE;
            }
        }

        // Now no more data need to be sent
        *pBuffLen = dwDataSent;
        RETAILMSG(UART_DEBUG,(TEXT("[UART]DMAWriteBuffer(): SENT 0x%X bytes...\r\n"),  (XLLP_INT16_T)dwDataSent));
    } //end else of "if (*pBuffLen == 0)"
    
    SetEvent(m_pMDDSerialHead->hTransmitEvent);
    m_HardwareLock.Unlock();
    return;
}


ULONG   CPXAPdd16550::PIO_ReceiveInterruptHandler(PUCHAR pRxBuffer,ULONG *pBufflen)
{
    DWORD dwBytesDropped = 0;

    if (pRxBuffer && pBufflen ) 
    {
        DWORD dwBytesStored = 0 ;
        DWORD dwRoomLeft = *pBufflen;
        m_bReceivedCanceled = FALSE;
        m_HardwareLock.Lock();
        
        while (dwRoomLeft && !m_bReceivedCanceled) 
        {
            UCHAR uLineStatus = GetLineStatus();
            if ((uLineStatus & SERIAL_LSR_DR)!=0 ) 
            {
                if (m_32BitBusEnabled)
                {
                    // 32-bit bus
                    *pBufflen = XllpUart32BitRx(&m_UART_Handle, (P_XLLP_UINT8_T)pRxBuffer, dwRoomLeft);
                    pRxBuffer += *pBufflen;
                    dwRoomLeft -= *pBufflen;
                    dwBytesStored += *pBufflen;
                } 
                else
                {
                    // 8-bit bus
                    UCHAR uData = m_pReg16550->Read_Data();
                    if (DataReplaced(&uData,(uLineStatus & SERIAL_LSR_PE)!=0)) {
                        *pRxBuffer++ = uData;
                        //RETAILMSG(UART_DEBUG, (TEXT("[UART] PIO Rx data: bytes[%d] = 0x%X\r\n"),dwBytesStored,uData)); 

                        dwRoomLeft--;
                        dwBytesStored++;                    
                    }
                } // end if (m_32BitBusEnabled)
            }
            else
                break;
        }
        
        if (m_bReceivedCanceled)
            dwBytesStored = 0;
        
        m_HardwareLock.Unlock();
        *pBufflen = dwBytesStored;
    }
    else 
    {
        ASSERT(FALSE);
    }
    
    return dwBytesDropped;
}

void    CPXAPdd16550::PIO_XmitInterruptHandler(PUCHAR pTxBuffer, ULONG *pBuffLen)
{
    PREFAST_DEBUGCHK(pBuffLen!=NULL);

    if(!pBuffLen)
    {
        RETAILMSG( 1, ( _T("[UART]PIO_XmitInterruptHandler ERROR, invalid parameter!") ) );
        return;
    }

    m_HardwareLock.Lock();
    
    if (*pBuffLen == 0)
    {
        EnableXmitInterrupt(FALSE);
    }
    else
    {
        DEBUGCHK(pTxBuffer);
        PulseEvent(m_XmitFlushDone);
        DWORD dwDataAvaiable = *pBuffLen;
        *pBuffLen = 0;
        
        if ((m_DCB.fOutxCtsFlow && IsCTSOff()) ||(m_DCB.fOutxDsrFlow && IsDSROff()))
        { 
            // We are in flow off
            DEBUGMSG(ZONE_THREAD|ZONE_WRITE,(TEXT("CPXAPdd16550::PIO_XmitInterruptHandler! Flow Off, Data Discard.\r\n")));
            EnableXmitInterrupt(FALSE);
        }
        else
        {

            if (m_32BitBusEnabled)
            {
                // 32bit bus
                *pBuffLen = XllpUart32BitTx(&m_UART_Handle, (P_XLLP_UINT8_T)pTxBuffer, dwDataAvaiable);
            }
            else
            {
                // 8bit bus
                DWORD dwWriteSize = GetWriteableSize();

                __try
                {
                    for (DWORD dwByteWrite=0; dwByteWrite<dwWriteSize && dwDataAvaiable!=0;dwByteWrite++) 
                    {
                        m_pReg16550->Write_DATA(*pTxBuffer);
                        //RETAILMSG(UART_DEBUG, (TEXT("[UART] PIO Tx data: bytes[%d] = 0x%X\r\n"),dwByteWrite,*pTxBuffer)); 
                        pTxBuffer ++;
                        dwDataAvaiable--;
                    }
                    *pBuffLen = dwByteWrite;
                }
                __except(EXCEPTION_EXECUTE_HANDLER)
                {
                    RETAILMSG( UART_DEBUG, ( _T("PIO_XmitInterruptHandler Exception!") ) );
                    ASSERT(FALSE);
                }
            } // end if (m_32BitBusEnabled)

            DEBUGMSG(ZONE_THREAD|ZONE_WRITE,(TEXT("CPXAPdd16550::PIO_XmitInterruptHandler! Write %d byte to FIFO\r\n"),*pBuffLen));
            EnableXmitInterrupt(TRUE);        
        }
    }
    
    m_HardwareLock.Unlock();
}

